package com.jin.webvideo.base.spi;

import com.jin.webvideo.base.spi.anno.InterfaceGetter;
import com.jin.webvideo.base.spi.anno.InterfaceVoidGetter;
import com.jin.webvideo.base.spi.exception.InterfaceNotFoundException;
import com.jin.webvideo.base.spi.exception.InterfaceRunningException;


public class Interfaces {
    public static  Interface parse(String group, String type) throws InterfaceNotFoundException {
        return getProvider(group).getInterface(type);
    }
    public static  InterfaceProvider getProvider(String group) throws InterfaceNotFoundException {
        if (group == null || group.isEmpty())
            throw new IllegalArgumentException("Missing group");

        // check for default provider to avoid loading of installed providers
        if (group.equalsIgnoreCase("test"))
            return InterfaceProvider.getDefault();

        // try to find provider
        for (InterfaceProvider provider: InterfaceProvider.installedProviders()) {
            if (group.equalsIgnoreCase(provider.getGroup())) {
                return provider;
            }
        }
        throw new InterfaceNotFoundException("Provider \"" + group + "\" not installed");
    }

    public static <T> T visit(Interface i, String... param) throws InterfaceRunningException {
        return (T) i.run(param);
    }

    public static <T, V> V visit(Interface i, InterfaceVisitor<T, V> visitor) throws InterfaceRunningException {
        T t = visit(i, visitor.params());
        return visitor.accept(t);
    }

    public static <T, V> V visit(Interface i, InterfaceGetter<T,V> getter, String... params) throws InterfaceRunningException {
        return visit(i, new InnerVisitor<T,V>(getter, params) );
    }

    public static <T, V> void visit(Interface i, InterfaceVoidGetter<T> getter, String... params) throws InterfaceRunningException {
        visit(i, new InnerVoidVisitor<T> (getter, params) );
    }
    private static class InnerVisitor<T, V> extends InterfaceVisitor<T, V>{
        private InterfaceGetter<T, V> getter;

        public InnerVisitor(InterfaceGetter<T,V> getter, String... param) {
            this.getter = getter;
            params(param);
        }
        @Override
        V accept(T t) {
            return getter.apply(t);
        }
    }
    private static class InnerVoidVisitor<T> extends InterfaceVisitor<T, String>{
        private InterfaceVoidGetter<T> getter;

        public InnerVoidVisitor(InterfaceVoidGetter<T> getter, String... param) {
            this.getter = getter;
            params(param);
        }

        @Override
        String accept(T t) {
            getter.apply(t);
            return null;
        }
    }
}
